import asyncio

from ._queue import ModelLogQueue
from typing import Optional, Dict, TypeVar, AnyStr
import time
from dataclasses import dataclass
from lib import singleton
from ._core import Model
from .userinfo import ModelUserInfo
from .hero import ModelHero

Uid = AnyStr
DbName = TypeVar("DbName")
DbInfo = Dict[str, str]


def now() -> int:
    return int(time.time())


@dataclass
class ModelInfo:
    model: Model
    last_time: int = now()

    def check_expire(self, nt: int = None):
        """检测model过期"""
        if nt is None:
            nt = now()

        if nt - self.last_time >= self.model.expire_time:
            return True

        return False

    def update_last_time(self):
        """更新最后一次访问时间"""
        self.last_time = now()

    async def clear(self, uid: Uid):
        """过期清除"""
        await self.model.clear(uid)


@singleton
class ModelManage(object):
    def __init__(self):
        self.log_queue = ModelLogQueue()
        self.UserInfo = ModelUserInfo(self)
        self.Hero = ModelHero(self)

        self.auto_clear_time: int = 15
        self.uid2DbName2Info: Dict[Uid, Dict[DbName, ModelInfo]] = {}

    async def run(self):
        asyncio.create_task(self.auto_clear())
        asyncio.create_task(self.auto_cache_clear())
        asyncio.create_task(self.log_queue.run())

    async def auto_clear(self):
        """清除过期model"""
        while True:
            await asyncio.sleep(self.auto_clear_time)
            nt = now()
            for uid, dbName2ModelInfo in self.uid2DbName2Info.items():
                del_db_name_list = []
                for db_name, modelInfo in dbName2ModelInfo.items():
                    # 还没过期
                    if not modelInfo.check_expire(nt):
                        continue

                    del_db_name_list.append(db_name)
                    # 清除
                    await modelInfo.clear(uid)

                for db_name in del_db_name_list:
                    del dbName2ModelInfo[db_name]

    async def auto_cache_clear(self):
        while True:
            await asyncio.sleep(self.auto_clear_time)
            for model in self.__dict__.items():
                if not isinstance(model, Model):
                    continue
                model.cache.chk_clear()

    def join(self, uid: Uid, model: Model, come_in: bool = False):
        """model访问检测"""
        if uid not in self.uid2DbName2Info:
            self.uid2DbName2Info[uid] = {}

        db_name = model.db_name
        modelInfo = self.uid2DbName2Info[uid].get(db_name)
        # 存在
        if modelInfo:
            # 更新时间
            if come_in:
                modelInfo.update_last_time()
            return

        self.uid2DbName2Info[uid][db_name] = ModelInfo(model=model)

    def update_last_time(self, uid: Uid, db_name: DbName):
        """更新model访问时间"""
        if uid not in self.uid2DbName2Info:
            return

        modelInfo = self.uid2DbName2Info[uid].get(db_name)
        if not modelInfo:
            return

        modelInfo.update_last_time()
